package io.github.talelin.latticy.service.oms.impl;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import io.github.talelin.autoconfigure.exception.ForbiddenException;
import io.github.talelin.latticy.common.pojo.BillPrefixConfigEntity;
import io.github.talelin.latticy.dto.oms.inbound_order.*;
import io.github.talelin.latticy.model.oms.*;
import io.github.talelin.latticy.mapper.oms.InboundPackagesMapper;
import io.github.talelin.latticy.service.IdGeneratorService;
import io.github.talelin.latticy.service.oms.InboundPackagesItemsService;
import io.github.talelin.latticy.service.oms.InboundPackagesService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.github.talelin.latticy.service.oms.ProductService;
import io.github.talelin.latticy.vo.ReceivingRecordVO;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author generator@TaleLin
 * @since 2022-05-18
 */
@Service
public class InboundPackagesServiceImpl extends ServiceImpl<InboundPackagesMapper, InboundPackagesDO> implements InboundPackagesService {

    @Autowired
    private BillPrefixConfigEntity billPrefixConfigEntity;

    @Autowired
    private InboundPackagesItemsService inboundPackagesItemsService;

    @Autowired
    private ProductService productService;

    @Autowired
    private IdGeneratorService idGeneratorService;

    @Autowired
    @Qualifier("threadPoolTaskExecutor")
    private Executor executor;

    @Transactional
    @Override
    public boolean saveInboundPackageDO(InboundPackagesDTO inboundPackagesDTO) {

        // 存储过程生成分布式唯一单号
        // redis生成分布式唯一单号
        String seqNo = idGeneratorService.generatorId(billPrefixConfigEntity.getPkg());

        InboundPackagesDO InboundPackagesDO = new InboundPackagesDO(inboundPackagesDTO, seqNo);
        this.save(InboundPackagesDO);
        List<Long> productIdList = inboundPackagesDTO.getInboundPackagesItemsList()
                .parallelStream().map(InboundPackagesItemsDTO::getProductId).collect(Collectors.toList());
        Optional.ofNullable(productIdList).orElseThrow(() -> new ForbiddenException(19002));
        List<ProductDO> productDOList = productService.getBaseMapper().selectBatchIds(productIdList);
        // 生成产品map
        Map<Long, ProductDO> productDOMap = productDOList.parallelStream().collect(Collectors.toMap(ProductDO::getId, productDO -> productDO));
        List<InboundPackagesItemsDTO> warehousePackagesItemDTOList = inboundPackagesDTO.getInboundPackagesItemsList();
        List<CompletableFuture<InboundPackagesItemsDO>> collect = warehousePackagesItemDTOList.stream().map(itemDTO -> CompletableFuture.supplyAsync(() -> itemDTO2ItemDO(itemDTO, seqNo, productDOMap, inboundPackagesDTO.getQuantity()), executor)).collect(Collectors.toList());
        List<InboundPackagesItemsDO> inboundPackagesItemsDOList = collect.stream().map(CompletableFuture::join).filter(Objects::nonNull).collect(Collectors.toList());
        return inboundPackagesItemsService.saveBatch(inboundPackagesItemsDOList);
    }

    private InboundPackagesItemsDO itemDTO2ItemDO(InboundPackagesItemsDTO itemDTO, String seqNo, Map<Long, ProductDO> productDOMap, Integer quantity) {
        ProductDO productDO = productDOMap.get(itemDTO.getProductId());
        InboundPackagesItemsDO InboundPackagesItemsDO = new InboundPackagesItemsDO();
        BeanUtils.copyProperties(itemDTO, InboundPackagesItemsDO);
        InboundPackagesItemsDO.setPackageNo(seqNo);
        InboundPackagesItemsDO.setPackageNum(quantity);
        InboundPackagesItemsDO.setSku(productDO.getSku());
        InboundPackagesItemsDO.setSkuName(productDO.getName());
        return InboundPackagesItemsDO;
    }

    @Override
    public void deletePackages(List<String> inboundOrderNoList) {
        QueryWrapper<InboundPackagesDO> packagesWrapper = new QueryWrapper<>();
        packagesWrapper.lambda().in(InboundPackagesDO::getInboundOrderNo, inboundOrderNoList);
        List<InboundPackagesDO> inboundPackagesList = getPackagesByOrderNoList(inboundOrderNoList);
        Optional.ofNullable(inboundPackagesList).orElseThrow(() -> new ForbiddenException(19002));
        List<String> packagesNoList = inboundPackagesList.parallelStream().map(InboundPackagesDO::getPackageNo).collect(Collectors.toList());
        this.remove(packagesWrapper);
        QueryWrapper<InboundPackagesItemsDO> packagesItemWrapper = new QueryWrapper<>();
        packagesItemWrapper.lambda().in(InboundPackagesItemsDO::getPackageNo, packagesNoList);
        inboundPackagesItemsService.remove(packagesItemWrapper);
    }

    @Override
    public List<InboundPackagesDO> getPackagesByOrderNoList(List<String> inboundOrderNoList) {
        QueryWrapper<InboundPackagesDO> packagesWrapper = new QueryWrapper<>();
        packagesWrapper.lambda().in(InboundPackagesDO::getInboundOrderNo, inboundOrderNoList);

        return this.baseMapper.selectList(packagesWrapper);
    }

    @Override
    public Map<String, List<InboundPackagesDO>> getPackagesMapByOrderNoList(List<String> inboundOrderNoList) {
        List<InboundPackagesDO> inboundPackagesDOS = this.getPackagesByOrderNoList(inboundOrderNoList);
        if (CollUtil.isEmpty(inboundPackagesDOS)) {
            throw new ForbiddenException(19002);
        }
        Map<String, List<InboundPackagesDO>> packagesMap = inboundPackagesDOS.parallelStream().collect(Collectors.groupingBy(InboundPackagesDO::getInboundOrderNo));
        List<String> packagesNoList = inboundPackagesDOS.parallelStream().map(InboundPackagesDO::getPackageNo).collect(Collectors.toList());
        Map<String, List<InboundPackagesItemsDO>> packagesItemsMap = inboundPackagesItemsService.getPackagesItemsMapByPackageNoList(packagesNoList);
        inboundOrderNoList.forEach(inboundOrderNo -> {
            List<InboundPackagesDO> inboundPackagesList = packagesMap.get(inboundOrderNo);
            if(CollectionUtils.isNotEmpty(inboundPackagesList)) {
                inboundPackagesList.forEach(inboundPackagesDO -> {
                    List<InboundPackagesItemsDO> inboundPackagesItemsList = packagesItemsMap.get(inboundPackagesDO.getPackageNo());
                    inboundPackagesDO.setInboundPackagesItemsList(inboundPackagesItemsList);
                });
            }
        });
        return packagesMap;
    }

    @Override
    public boolean batchUpdateReceiveQuantityByIds(List<ReceivingPackageDTO> packages) {
        return this.baseMapper.batchUpdateReceiveQuantityByIds(packages) > 0;
    }

    @Override
    public boolean updateReceiveQuantityByPackageNo(ReceivingRecordVO receivingRecordVO) {
        UpdateWrapper updateWrapper = new UpdateWrapper();
        updateWrapper.eq("package_no", receivingRecordVO.getPackageNo());
        updateWrapper.setSql("receive_quantity = receive_quantity -" + receivingRecordVO.getReceiveNum());
        return this.update(updateWrapper);
    }
}
